/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.jellyfishumbrella.GL;
import org.lwjgl.opengl.*;
import org.lwjgl.opengl.GL20.*;
import org.lwjgl.opengl.GL33.*;
import java.io.BufferedReader;
import java.io.FileReader;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ARBVertexShader;
import org.lwjgl.opengl.ARBFragmentShader;
import org.lwjgl.opengl.Util;


/**
 *
 * @author Rich Helvey
 */
public class Shaders {
    public int vertShader = 0;
    public int fragShader = 0;
    public int shader = 0;
    
    public Boolean useShader;

    public Shaders(String vertShaderFile, String fragShaderFile){
        /*
        * create the shader program. If OK, create vertex
        * and fragment shaders
        */
        shader = GL20.glCreateProgram();

        if(shader!=0){
            vertShader=createVertShader(vertShaderFile);
            fragShader=createFragShader(fragShaderFile);
        }
        else useShader=false;

        /*
        * if the vertex and fragment shaders setup sucessfully,
        * attach them to the shader program, link the shader program
        * (into the GL context I suppose), and validate
        */
        if(vertShader !=0 && fragShader !=0){
            GL20.glAttachShader(shader, vertShader);
            GL20.glAttachShader(shader, fragShader);
            GL20.glValidateProgram(shader);

//	    for(int i = 0; i < attribNames.length; i++){
//		GL20.glBindAttribLocation(shader,attribSlots[i],attribNames[i]);
//	    }

            GL20.glLinkProgram(shader);
            GL20.glValidateProgram(shader);
            useShader=true;
        }else{
            System.out.println("shader failure!");
            useShader=false;
        }

    }

    public void Shaders2(String vertShaderFile, String fragShaderFile, String[] attribNames, int[] attribSlots){
        /*
        * create the shader program. If OK, create vertex
        * and fragment shaders
        */
        shader = GL20.glCreateProgram();

        if(shader!=0){
            vertShader=createVertShader(vertShaderFile);
            fragShader=createFragShader(fragShaderFile);
        }
        else useShader=false;

        /*
        * if the vertex and fragment shaders setup sucessfully,
        * attach them to the shader program, link the shader program
        * (into the GL context I suppose), and validate
        */
        if(vertShader !=0 && fragShader !=0){
            GL20.glAttachShader(shader, vertShader);
            GL20.glAttachShader(shader, fragShader);
            GL20.glValidateProgram(shader);

	    for(int i = 0; i < attribNames.length; i++){
		GL20.glBindAttribLocation(shader,attribSlots[i],attribNames[i]);
	    }

            GL20.glLinkProgram(shader);
            GL20.glValidateProgram(shader);
            useShader=true;
        }else{
            System.out.println("shader failure!");
            useShader=false;
        }

    }

    public void use(){
        ARBShaderObjects.glUseProgramObjectARB(shader);
    }

    public void release(){
        ARBShaderObjects.glUseProgramObjectARB(0);
    }

    /*
    * With the exception of syntax, setting up vertex and fragment shaders
    * is the same.
    * @param the name and path to the vertex shader
    */
    private int createVertShader(String filename){
        //vertShader will be non zero if succefully created

        vertShader=ARBShaderObjects.glCreateShaderObjectARB(ARBVertexShader.GL_VERTEX_SHADER_ARB);
        //if created, convert the vertex shader code to a String
        if(vertShader==0){return 0;}
        String vertexCode="";
        String line;
        try{
            BufferedReader reader=new BufferedReader(new FileReader(filename));
            while((line=reader.readLine())!=null){
                vertexCode+=line + "\n";
            }
        }catch(Exception e){
            System.out.println("Fail reading vertex shading code");
            return 0;
        }
        /*
        * associate the vertex code String with the created vertex shader
        * and compile
        */
        ARBShaderObjects.glShaderSourceARB(vertShader, vertexCode);
        ARBShaderObjects.glCompileShaderARB(vertShader);

        //if there was a problem compiling, reset vertShader to zero
        if(!printLogInfo(vertShader)){
            //vertShader=0;
        }
        //if zero we won't be using the shader
        return vertShader;
    }

    //same as per the vertex shader except for method syntax
    private int createFragShader(String filename){

        fragShader=ARBShaderObjects.glCreateShaderObjectARB(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);
        if(fragShader==0){return 0;}
            String fragCode="";
            String line;
        try{
            BufferedReader reader=new BufferedReader(new FileReader(filename));
            while((line=reader.readLine())!=null){
                fragCode+=line + "\n";
            }
        }catch(Exception e){
            System.out.println("Fail reading fragment shading code");
            return 0;
        }
        ARBShaderObjects.glShaderSourceARB(fragShader, fragCode);
        ARBShaderObjects.glCompileShaderARB(fragShader);

        if(!printLogInfo(fragShader)){
            //fragShader=0;
        }

        return fragShader;
    }
        /*
    * oddly enough, checking the success when setting up the shaders is
    * verbose upon success. If the reference iVal becomes greater
    * than 1, the setup being examined (obj) has been successful, the
    * information gets printed to System.out, and true is returned.
    */
    private  boolean printLogInfo(int obj){
        IntBuffer iVal = BufferUtils.createIntBuffer(1);
        ARBShaderObjects.glGetObjectParameterARB(obj,
        ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB, iVal);
        
        int length = iVal.get();
        if (length > 1) {
            // We have some info we need to output.
            ByteBuffer infoLog = BufferUtils.createByteBuffer(length);
            iVal.flip();
            ARBShaderObjects.glGetInfoLogARB(obj, iVal, infoLog);
            byte[] infoBytes = new byte[length];
            infoLog.get(infoBytes);
            String out = new String(infoBytes);
            System.out.println("Info log:\n"+out);
        }
        else return false;
        return true;
    }

}
